home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sprite 1984 - 1993
/
Sprite 1984 - 1993.iso
/
src
/
kernel
/
fsrmt
/
fsrmtFile.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-18
|
53KB
|
1,716 lines
/*
* fsRmtFile.c --
*
* Routines for operations on remote files. The I/O operations
* check in the cache, and then use the raw remote I/O operations
* to transfer data to and from the remote file server.
*
* Copyright 1987 Regents of the University of California
* All rights reserved.
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*/
#ifndef lint
static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/fsrmt/fsrmtFile.c,v 1.14 92/12/13 18:17:01 mgbaker Exp $ SPRITE (Berkeley)";
#endif not lint
#include <sprite.h>
#include <fs.h>
#include <fsutil.h>
#include <fsconsist.h>
#include <fsioFile.h>
#include <fslcl.h>
#include <fscache.h>
#include <fsdm.h>
#include <fsStat.h>
#include <fsprefix.h>
#include <fsNameOps.h>
#include <fsrmt.h>
#include <recov.h>
#include <rpc.h>
#include <vm.h>
#include <stdio.h>
static ReturnStatus FsrmtFileBlockRead _ARGS_((Fs_HandleHeader *hdrPtr,
Fscache_Block *blockPtr, Sync_RemoteWaiter *waitPtr));
static ReturnStatus FsrmtFileBlockWrite _ARGS_((Fs_HandleHeader *hdrPtr,
Fscache_Block *blockPtr, int flags));
static ReturnStatus FsrmtFileBlockAllocate _ARGS_((Fs_HandleHeader *hdrPtr,
int offset, int numBytes, int flags, int *blockAddrPtr,
Boolean *newBlockPtr));
static ReturnStatus FsrmtFileTrunc _ARGS_((Fs_HandleHeader *hdrPtr, int size,
Boolean delete));
/* Second param for ASPLOS only. Remove
* when that's over. -Mary 2/15/92. */
static Boolean FsrmtStartWriteBack _ARGS_((Fscache_Backend *backendPtr, Boolean fileFsynced));
static void FsrmtReallocBlock _ARGS_((ClientData data,
Proc_CallInfo *callInfoPtr));
static Fscache_BackendRoutines fsrmtBackendRoutines = {
FsrmtFileBlockAllocate,
FsrmtFileTrunc,
FsrmtFileBlockRead,
FsrmtFileBlockWrite,
FsrmtReallocBlock,
FsrmtStartWriteBack,
};
static Fscache_Backend *cacheBackendPtr = (Fscache_Backend *) NIL;
static Boolean FileMatch _ARGS_((Fscache_FileInfo *cacheInfoPtr,
ClientData cleintData));
static Boolean BlockMatch _ARGS_((Fscache_Block *blockPtr,
ClientData cleintData));
static Sync_Lock rmtCleanerLock = Sync_LockInitStatic("Fs:rmtCleanerLock");
#define LOCKPTR &rmtCleanerLock
int fsrmtBlockCleaners = 0;
/*
*----------------------------------------------------------------------
*
* FsRmtFileHandleInit --
*
* Initialize a handle for a remote file from the file state
* returned by the server.
*
* Results:
* SUCCESS.
*
* Side effects:
* Create and install a handle for the file.
*
*----------------------------------------------------------------------
*/
ReturnStatus
FsRmtFileHandleInit(fileIDPtr, fileStatePtr, openForWriting, name,
newHandlePtrPtr)
Fs_FileID *fileIDPtr;
Fsio_FileState *fileStatePtr;
Boolean openForWriting;
char *name;
Fsrmt_FileIOHandle **newHandlePtrPtr;
{
register Fsrmt_FileIOHandle *handlePtr;
Boolean found;
int size;
/*
* Since both Fsrmt_FileIOHandle and Fsio_FileIOHandle are so
* popular on the root file server we try to use the same memory
* size for both. We choose to allocate the large of the two.
*/
size = sizeof(Fsrmt_FileIOHandle);
if (size < sizeof(Fsio_FileIOHandle)) {
size = sizeof(Fsio_FileIOHandle);
}
found = Fsutil_HandleInstall(fileIDPtr, size, name,
FALSE, (Fs_HandleHeader **)newHandlePtrPtr);
handlePtr = *newHandlePtrPtr;
if (found) {
/*
* Update attributes cached in the handle, and verify the
* validity of any cached data blocks.
*/
if (Fscache_UpdateFile(&handlePtr->cacheInfo, openForWriting,
fileStatePtr->version, fileStatePtr->cacheable,
&fileStatePtr->attr)) {
Vm_FileChanged(&handlePtr->segPtr);
}
/*
* Update the handle's open time stamp. This is used to catch races
* between near-simultanous opens on a client and related cache
* consistency messasge.
*/
handlePtr->openTimeStamp = fileStatePtr->openTimeStamp;
} else {
if (cacheBackendPtr == (Fscache_Backend *) NIL) {
cacheBackendPtr =
Fscache_RegisterBackend(&fsrmtBackendRoutines,(ClientData)0,0);
}
/*
* Initialize the new handle. There is no cache validation to
* be done because blocks are only cached when a handle is present.
*/
Fscache_FileInfoInit(&handlePtr->cacheInfo, (Fs_HandleHeader *)handlePtr,
fileStatePtr->version, fileStatePtr->cacheable,
&fileStatePtr->attr, cacheBackendPtr);
Fsutil_RecoveryInit(&handlePtr->rmt.recovery);
Fscache_ReadAheadInit(&handlePtr->readAhead);
handlePtr->flags = 0;
handlePtr->openTimeStamp = fileStatePtr->openTimeStamp;
handlePtr->segPtr = (Vm_Segment *)NIL;
fs_Stats.object.rmtFiles++;
}
if (fileStatePtr->newUseFlags & FS_DIR) {
handlePtr->cacheInfo.flags |= FSCACHE_IS_DIR;
}
free((Address)fileStatePtr);
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* FsrmtFileIoOpen --
*
* Set up a stream for a remote disk file. This is called from Fs_Open
* to complete the opening of a stream. By this time any cache consistency
* actions have already been taken.
*
* Results:
* SUCCESS, until FsRmtFileHandleInit returns differently.
*
* Side effects:
* Installs the handle for the file and updates the use counts
* (ref, write, exec) due to this open.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
FsrmtFileIoOpen(ioFileIDPtr, flagsPtr, clientID, streamData, name,
ioHandlePtrPtr)
Fs_FileID *ioFileIDPtr; /* I/O fileID from the name server */
int *flagsPtr; /* New ones from the server returned */
int clientID; /* IGNORED */
ClientData streamData; /* Reference to Fsio_FileState struct */
char *name; /* File name for error msgs */
Fs_HandleHeader **ioHandlePtrPtr;/* Return - a handle set up for
* I/O to a file, NIL if failure. */
{
ReturnStatus status;
Fsio_FileState *fileStatePtr;
register int flags;
register Fsrmt_FileIOHandle *rmtHandlePtr;
fileStatePtr = (Fsio_FileState *)streamData;
flags = fileStatePtr->newUseFlags;
status = FsRmtFileHandleInit(ioFileIDPtr, fileStatePtr,
(flags & FS_WRITE), name, (Fsrmt_FileIOHandle **)ioHandlePtrPtr);
if (status == SUCCESS) {
rmtHandlePtr = (Fsrmt_FileIOHandle *)*ioHandlePtrPtr;
/*
* Update our use information to reflect the open.
*/
rmtHandlePtr->rmt.recovery.use.ref++;
if (flags & FS_WRITE) {
rmtHandlePtr->rmt.recovery.use.write++;
}
if (flags & FS_EXECUTE) {
rmtHandlePtr->rmt.recovery.use.exec++;
}
/*
* Note if we are a swap file in case of recovery later.
*/
rmtHandlePtr->flags |= (flags & FS_SWAP);
*flagsPtr = flags;
Fsutil_HandleUnlock(rmtHandlePtr);
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* FsrmtFileReopen --
*
* Reopen a remote file. This sets up and conducts an
* RPC_FS_REOPEN remote procedure call to re-open the remote file.
*
* Results:
* A non-SUCCESS return code if the re-open was attempted and failed.
* FS_NO_HANDLE if we delete the handle.
*
* Side effects:
* If the reopen works we'll have updated cached attributes, version
* number, etc.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
FsrmtFileReopen(hdrPtr, clientID, inData, outSizePtr, outDataPtr)
Fs_HandleHeader *hdrPtr;
int clientID; /* Should be rpc_SpriteID */
ClientData inData; /* IGNORED */
int *outSizePtr; /* IGNORED */
ClientData *outDataPtr; /* IGNORED */
{
register Fsrmt_FileIOHandle *rmtHandlePtr;
ReturnStatus status;
Fsio_FileReopenParams reopenParams;
Fsio_FileState fileState;
register int numDirtyBlocks;
int outSize;
rmtHandlePtr = (Fsrmt_FileIOHandle *)hdrPtr;
numDirtyBlocks = Fscache_PreventWriteBacks(&rmtHandlePtr->cacheInfo);
Fscache_AllowWriteBacks(&rmtHandlePtr->cacheInfo);
/*
* Optimize out re-opens for files that aren't being waited
* on, that have no users, and that have no blocks in the cache.
* I don't call OkToScavenge from fsutilHandleScavenge.c, since I think
* it's okay to do this even if there's already an active scavenger,
* because it will have locked the handle if it's working on it.
*/
if (recov_SkipCleanFiles && (rmtHandlePtr->rmt.recovery.use.ref == 0 &&
Fscache_OkToScavengeExceptDirty(&rmtHandlePtr->cacheInfo) &&
!Fsutil_RecoveryNeeded(&rmtHandlePtr->rmt.recovery))) {
fs_Stats.recovery.reopensAvoided++;
return FS_RECOV_SKIP;
} else if (!recov_SkipCleanFiles &&
(rmtHandlePtr->rmt.recovery.use.ref == 0 &&
Fscache_OkToScavengeExceptDirty(&rmtHandlePtr->cacheInfo) &&
!Fsutil_RecoveryNeeded(&rmtHandlePtr->rmt.recovery))) {
Vm_FileChanged(&rmtHandlePtr->segPtr);
Fsutil_RecoverySyncLockCleanup(&rmtHandlePtr->rmt.recovery);
Fscache_InfoSyncLockCleanup(&rmtHandlePtr->cacheInfo);
Fscache_ReadAheadSyncLockCleanup(&rmtHandlePtr->readAhead);
Fsutil_HandleRemove(rmtHandlePtr);
fs_Stats.object.rmtFiles--;
fs_Stats.recovery.reopensAvoided++;
return FS_NO_HANDLE;
}
/* for debugging */
#define FS_HANDLE_INVALID 0x20
if (hdrPtr->flags & FS_HANDLE_INVALID) {
printf("Attempting to recover invalid file handle <%d,0x%x,0x%x>\n",
hdrPtr->fileID.serverID, hdrPtr->fileID.major,
hdrPtr->fileID.minor);
return SUCCESS;
}
/* end for debugging */
reopenParams.flags = rmtHandlePtr->flags;
if (numDirtyBlocks > 0) {
reopenParams.flags |= FSIO_HAVE_BLOCKS;
}
reopenParams.fileID = hdrPtr->fileID;
reopenParams.fileID.type = FSIO_LCL_FILE_STREAM;
reopenParams.prefixFileID.type = NIL; /* not used */
reopenParams.use = rmtHandlePtr->rmt.recovery.use;
reopenParams.version = rmtHandlePtr->cacheInfo.version;
/*
* Contact the server to do the reopen. We have to unlock the handle
* here in case the server asks us to write-back or invalidate.
*/
outSize = sizeof(Fsio_FileState);
Fsutil_HandleUnlock(hdrPtr);
status = FsrmtReopen(hdrPtr, sizeof(Fsio_FileReopenParams),
(Address)&reopenParams, &outSize, (Address)&fileState);
Fsutil_HandleLock(hdrPtr);
if (status != SUCCESS) {
if (numDirtyBlocks > 0) {
printf(
"Re-open failed <%x> with dirty blocks in cache, \"%s\" <%d,%d> server %d\n",
status, Fsutil_HandleName(hdrPtr), hdrPtr->fileID.major,
hdrPtr->fileID.minor, hdrPtr->fileID.serverID);
}
if (status != RPC_TIMEOUT &&
status != RPC_SERVICE_DISABLED) {
/*
* Nuke the cache because our caller will nuke the handle.
*/
Fscache_FileInvalidate(&rmtHandlePtr->cacheInfo, 0, FSCACHE_LAST_BLOCK);
}
} else {
/*
* Flush any lanquishing dirty blocks back to the server.
*/
if (numDirtyBlocks > 0) {
int skipped;
(void) Fscache_FileWriteBack(&rmtHandlePtr->cacheInfo, 0,
FSCACHE_LAST_BLOCK, 0, &skipped);
}
/*
* Update the handle - take care of cache flushes, updating cached
* attributes, etc.
*/
if (Fscache_UpdateFile(&rmtHandlePtr->cacheInfo,
rmtHandlePtr->rmt.recovery.use.write,
fileState.version, fileState.cacheable,
&fileState.attr)) {
Vm_FileChanged(&rmtHandlePtr->segPtr);
}
rmtHandlePtr->openTimeStamp = fileState.openTimeStamp;
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* FsrmtFileClose --
*
* Close time processing for remote files.
*
* Results:
* SUCCESS.
*
* Side effects:
* This decrements the local use counts, then synchronizes
* with write-backs before telling the server about the close.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
FsrmtFileClose(streamPtr, clientID, procID, flags, dataSize, closeData)
Fs_Stream *streamPtr; /* Stream to remote file */
int clientID; /* HostID of client closing */
Proc_PID procID; /* Process ID of closer */
int flags; /* Flags from the stream being closed */
int dataSize; /* Size of closeData */
ClientData closeData; /* NIL on entry. */
{
register ReturnStatus status;
register Fsrmt_FileIOHandle *handlePtr =
(Fsrmt_FileIOHandle *)streamPtr->ioHandlePtr;
/*
* Decrement local references.
*/
handlePtr->rmt.recovery.use.ref--;
if (flags & FS_WRITE) {
handlePtr->rmt.recovery.use.write--;
}
if (flags & FS_EXECUTE) {
handlePtr->rmt.recovery.use.exec--;
}
if (handlePtr->rmt.recovery.use.ref < 0 ||
handlePtr->rmt.recovery.use.write < 0 ||
handlePtr->rmt.recovery.use.exec < 0) {
panic("FsrmtFileClose: <%d,%d> ref %d write %d exec %d\n",
handlePtr->rmt.hdr.fileID.major, handlePtr->rmt.hdr.fileID.minor,
handlePtr->rmt.recovery.use.ref,
handlePtr->rmt.recovery.use.write,
handlePtr->rmt.recovery.use.exec);
}
if (!Fsutil_HandleValid((Fs_HandleHeader *)handlePtr)) {
status = FS_FILE_REMOVED;
} else {
if (handlePtr->rmt.recovery.use.ref == 0) {
int numDirtyBlocks;
/*
* Synchronize closes and delayed writes. We wait for writes
* in progress to complete.
*/
numDirtyBlocks = Fscache_PreventWriteBacks(&handlePtr->cacheInfo);
if (numDirtyBlocks == 0) {
/*
* Inform the server on the close requests the we don't
* have any dirty blocks for the file.
*/
flags |= FS_LAST_DIRTY_BLOCK;
}
}
status = Fsrmt_Close(streamPtr, clientID, procID, flags,
sizeof(Fscache_Attributes), (ClientData)&handlePtr->cacheInfo.attr);
}
if (status == FS_FILE_REMOVED && handlePtr->rmt.recovery.use.ref == 0) {
/*
* Nuke our cache after we've finished with this (now bogus) handle.
* (Apparently we let the handle linger and get scavenged. Could
* change things to remove the handle here.)
*/
Fscache_Trunc(&handlePtr->cacheInfo, 0, FSCACHE_TRUNC_DELETE);
}
Fscache_AllowWriteBacks(&handlePtr->cacheInfo);
Fsutil_HandleRelease(handlePtr, TRUE);
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* FsrmtFileScavenge --
*
* Called peridocally to see if we still need the handle for
* the remote file.
*
* Results:
* TRUE if it removed the handle.
*
* Side effects:
* Either removes or unlocks the handle.
*
*----------------------------------------------------------------------
*/
Boolean
FsrmtFileScavenge(hdrPtr)
Fs_HandleHeader *hdrPtr;
{
register Fsrmt_FileIOHandle *handlePtr = (Fsrmt_FileIOHandle *)hdrPtr;
if (handlePtr->rmt.recovery.use.ref == 0 &&
Fscache_OkToScavenge(&handlePtr->cacheInfo) &&
!Fsutil_RecoveryNeeded(&handlePtr->rmt.recovery)) {
/*
* Remove handles for files with no users and no blocks in cache.
*/
Vm_FileChanged(&handlePtr->segPtr);
Fsutil_RecoverySyncLockCleanup(&handlePtr->rmt.recovery);
Fscache_InfoSyncLockCleanup(&handlePtr->cacheInfo);
Fscache_ReadAheadSyncLockCleanup(&handlePtr->readAhead);
Fsutil_HandleRemove(handlePtr);
fs_Stats.object.rmtFiles--;
return(TRUE);
} else {
Fsutil_HandleUnlock(handlePtr);
return(FALSE);
}
}
/*
*----------------------------------------------------------------------
*
* FsrmtFileVerify --
*
* Map from a remote client's fileID to a local fileID and
* verify that the remote client is known.
*
* Results:
* A pointer to the local handle for the file, or NIL if
* the client is bad.
*
* Side effects:
* Changes the client's fileID type to FSIO_LCL_FILE_STREAM and
* fetches the local handle. The handle is returned locked.
*
*----------------------------------------------------------------------
*/
Fs_HandleHeader *
FsrmtFileVerify(fileIDPtr, clientID, domainTypePtr)
Fs_FileID *fileIDPtr; /* Client's fileID */
int clientID; /* Host ID of the client */
int *domainTypePtr; /* Return - FS_LOCAL_DOMAIN */
{
register Fsio_FileIOHandle *handlePtr;
register Fsconsist_ClientInfo *clientPtr;
Boolean found = FALSE;
fileIDPtr->type = FSIO_LCL_FILE_STREAM;
handlePtr = Fsutil_HandleFetchType(Fsio_FileIOHandle, fileIDPtr);
if (handlePtr != (Fsio_FileIOHandle *)NIL) {
LIST_FORALL(&handlePtr->consist.clientList, (List_Links *) clientPtr) {
if (clientPtr->clientID == clientID) {
found = TRUE;
break;
}
}
if (!found) {
printf(
"FsrmtFileVerify: \"%s\" <%d,%d> client %d not found\n",
Fsutil_HandleName(handlePtr), fileIDPtr->major, fileIDPtr->minor,
clientID);
Fsutil_HandleRelease(handlePtr, TRUE);
handlePtr = (Fsio_FileIOHandle *)NIL;
}
} else {
printf( "FsrmtFileVerify, no handle <%d,%d> client %d\n",
fileIDPtr->major, fileIDPtr->minor, clientID);
}
if (domainTypePtr != (int *)NIL) {
*domainTypePtr = FS_LOCAL_DOMAIN;
}
return((Fs_HandleHeader *)handlePtr);
}
/*
* ----------------------------------------------------------------------------
*
* FsrmtFileMigClose --
*
* Release recovery use counts on a remote file. This is called when
* a stream to the file has migrated away.
*
* Results:
* SUCCESS.
*
* Side effects:
* Decrement use counts and release the handle.
*
* ----------------------------------------------------------------------------
*
*/
/*ARGSUSED*/
ReturnStatus
FsrmtFileMigClose(hdrPtr, flags)
Fs_HandleHeader *hdrPtr; /* File being encapsulated */
int flags; /* Use flags from the stream */
{
register Fsrmt_FileIOHandle *handlePtr = (Fsrmt_FileIOHandle *)hdrPtr;
Fsutil_HandleLock(handlePtr);
handlePtr->rmt.recovery.use.ref--;
if (flags & FS_WRITE) {
handlePtr->rmt.recovery.use.write--;
}
if (flags & FS_EXECUTE) {
handlePtr->rmt.recovery.use.exec--;
}
Fsutil_HandleRelease(handlePtr, TRUE);
return(SUCCESS);
}
/*
* ----------------------------------------------------------------------------
*
* FsrmtFileMigOpen --
*
* Complete the creation of a FSIO_RMT_FILE_STREAM after migration.
*
* Results:
* SUCCESS (until FsRmtFileHandleInit returns differently)
*
* Side effects:
* If SUCCESS, adds a reference and useCounts to the I/O handle.
*
* ----------------------------------------------------------------------------
*
*/
/*ARGSUSED*/
ReturnStatus
FsrmtFileMigOpen(migInfoPtr, size, data, hdrPtrPtr)
Fsio_MigInfo *migInfoPtr; /* Migration state */
int size; /* sizeof(Fsio_FileState), IGNORED */
ClientData data; /* Ref. to Fsio_FileState. */
Fs_HandleHeader **hdrPtrPtr; /* Return - I/O handle for the file */
{
ReturnStatus status;
register Fs_FileID *fileIDPtr = &migInfoPtr->ioFileID;
Fsrmt_FileIOHandle *handlePtr;
status = FsrmtFileIoOpen(fileIDPtr, &migInfoPtr->flags, rpc_SpriteID,
data, (char *)NIL, (Fs_HandleHeader **)&handlePtr);
*hdrPtrPtr = (Fs_HandleHeader *)handlePtr;
return(status);
}
/*
* ----------------------------------------------------------------------------
*
* FsrmtFileMigrate --
*
* This causes a call to Fsio_FileMigrate on the server to shift
* client references. We may be the server after migration, in which
* case the routine is called directly. Otherwise an RPC is done
* to the server. A useful side-effect of this routine is
* to properly set the type in the ioFileID, either FSIO_LCL_FILE_STREAM
* or FSIO_RMT_FILE_STREAM.
*
* Results:
* The return code from Fsio_FileMigrate, or the RPC.
* Returns Fsio_FileState for use by the MigEnd routine.
*
* Side effects:
* Sets the correct stream type on the ioFileID.
*
* ----------------------------------------------------------------------------
*
*/
/*ARGSUSED*/
ReturnStatus
FsrmtFileMigrate(migInfoPtr, dstClientID, flagsPtr, offsetPtr, sizePtr, dataPtr)
Fsio_MigInfo *migInfoPtr; /* Migration state */
int dstClientID; /* ID of target client */
int *flagsPtr; /* In/Out Stream usage flags */
int *offsetPtr; /* Return - the correct stream offset */
int *sizePtr; /* Return - sizeof(Fsio_FileState) */
Address *dataPtr; /* Return - pointer to Fsio_FileState */
{
register ReturnStatus status;
register Fsio_FileState *fileStatePtr;
if (migInfoPtr->ioFileID.serverID == rpc_SpriteID) {
/*
* The file was remote, which is why we were called, but is now local.
*/
migInfoPtr->ioFileID.type = FSIO_LCL_FILE_STREAM;
return(Fsio_FileMigrate(migInfoPtr, dstClientID, flagsPtr, offsetPtr,
sizePtr, dataPtr));
}
migInfoPtr->ioFileID.type = FSIO_RMT_FILE_STREAM;
fileStatePtr = mnew(Fsio_FileState);
status = Fsrmt_NotifyOfMigration(migInfoPtr, flagsPtr, offsetPtr,
sizeof(Fsio_FileState), (Address)fileStatePtr);
if (status != SUCCESS) {
printf( "FsrmtFileMigrate, server error <%x>\n",
status);
} else {
*dataPtr = (Address)fileStatePtr;
*sizePtr = sizeof(Fsio_FileState);
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* FsrmtFileRead --
*
* Read from a remote file. This tries the cache first, and then
* goes remote if the file is not cacheable. This also checks against
* remotely shared streams (due to migration), and bypasses the cache
* in that case.
*
* Results:
* The results of Fscache_Read, or of the RPC.
*
* Side effects:
* The *offsetPtr is updated to reflect the read.
*
*----------------------------------------------------------------------
*/
ReturnStatus
FsrmtFileRead(streamPtr, readPtr, remoteWaitPtr, replyPtr)
Fs_Stream *streamPtr; /* Stream to a remote file. */
Fs_IOParam *readPtr; /* Read parameter block. */
Sync_RemoteWaiter *remoteWaitPtr; /* Process info for remote waiting */
Fs_IOReply *replyPtr; /* Signal to return, if any,
* plus the amount read. */
{
register Fsrmt_FileIOHandle *handlePtr =
(Fsrmt_FileIOHandle *)streamPtr->ioHandlePtr;
register ReturnStatus status;
if (readPtr->flags & FS_RMT_SHARED) {
/*
* The stream is shared accross the network. We have to go through
* to the file server to get the correct stream offset.
*/
status = FS_NOT_CACHEABLE;
} else {
status = Fscache_Read(&handlePtr->cacheInfo, readPtr->flags,
readPtr->buffer, readPtr->offset, &readPtr->length,
remoteWaitPtr);
replyPtr->length = readPtr->length;
}
/*
* If not-cacheable then go to the remote file server. Otherwise
* update the amount read in the reply because Fscache_Read uses
* the old-style in/out length variable.
*/
if (status != FS_NOT_CACHEABLE) {
replyPtr->length = readPtr->length;
} else {
status = Fsrmt_Read(streamPtr, readPtr, remoteWaitPtr, replyPtr);
if (status == SUCCESS) {
if (readPtr->flags & FS_RMT_SHARED) {
fs_Stats.rmtIO.sharedStreamBytesRead += replyPtr->length;
} else {
fs_Stats.rmtIO.uncacheableBytesRead += replyPtr->length;
if (handlePtr->cacheInfo.flags & FSCACHE_IS_DIR) {
fs_Stats.rmtIO.uncacheableDirBytesRead += replyPtr->length;
}
}
}
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* FsrmtFileWrite --
*
* Write to a remote file. This checks for cachability and uses
* either the cache write routine or an RPC stub.
*
* Results:
* The results of Fscache_Write or the RPC.
*
* Side effects:
* A buffer may have to allocated when doing an uncached write.
*
*----------------------------------------------------------------------
*/
ReturnStatus
FsrmtFileWrite(streamPtr, writePtr, remoteWaitPtr, replyPtr)
Fs_Stream *streamPtr; /* Open stream to remote file. */
Fs_IOParam *writePtr; /* Read parameter block */
Sync_RemoteWaiter *remoteWaitPtr; /* Process info for remote waiting */
Fs_IOReply *replyPtr; /* Signal to return, if any */
{
register Fsrmt_FileIOHandle *handlePtr =
(Fsrmt_FileIOHandle *)streamPtr->ioHandlePtr;
register ReturnStatus status;
if (writePtr->flags & FS_RMT_SHARED) {
status = FS_NOT_CACHEABLE;
} else {
Fscache_WaitForReadAhead(&handlePtr->readAhead);
status = Fscache_Write(&handlePtr->cacheInfo, writePtr->flags,
writePtr->buffer, writePtr->offset,
&writePtr->length, remoteWaitPtr);
writePtr->flags &= ~FS_SERVER_WRITE_THRU;
Fscache_AllowReadAhead(&handlePtr->readAhead);
}
if (status != FS_NOT_CACHEABLE) {
replyPtr->length = writePtr->length;
} else {
status = Fsrmt_Write(streamPtr, writePtr, remoteWaitPtr, replyPtr);
if (status == SUCCESS) {
if (writePtr->flags & FS_RMT_SHARED) {
fs_Stats.rmtIO.sharedStreamBytesWritten += replyPtr->length;
} else {
fs_Stats.rmtIO.uncacheableBytesWritten += replyPtr->length;
}
}
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* FsrmtFilePageRead --
*
* Do a page-in for a remote file.
*
* Results:
* The results of Fscache_Read, or of Fsrmt_Read.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ReturnStatus
FsrmtFilePageRead(streamPtr, readPtr, remoteWaitPtr, replyPtr)
Fs_Stream *streamPtr; /* Stream to a remote file. */
Fs_IOParam *readPtr; /* Read parameter block. */
Sync_RemoteWaiter *remoteWaitPtr; /* Process info for remote waiting */
Fs_IOReply *replyPtr; /* Signal to return, if any,
* plus the amount read. */
{
register Fsrmt_FileIOHandle *handlePtr =
(Fsrmt_FileIOHandle *)streamPtr->ioHandlePtr;
register ReturnStatus status = SUCCESS;
int savedOffset = readPtr->offset;
int savedLength = readPtr->length;
Address savedBuffer = readPtr->buffer;
int blockNum, firstBlock, lastBlock, size, toRead;
Fscache_Block *blockPtr;
Boolean found;
/*
* This should check if the file is cached if:
* a) it is not a FS_SWAP file or
* b) it is a shared file (which will be FS_SWAP)
* but for now we'll just always check. - Ken
*
* This used to be
* if ((readPtr->flags & FS_SWAP) == 0) {
* The problem is that shared files are marked swap, and we don't want
* to look in the cache for regular swap files, but we must for shared
* files. We need another flag and there isn't one. If we know it's
* a swap page, we should just do an Fsrmt_Read.
*/
/*
* This may be a CODE or HEAP page, so we check in our cache
* to see if we have it there; we may have just written it.
* We are now, once again, attempting to special-case HEAP pages.
* We want to keep the clean copies in the cache so they can
* be re-read.
*/
size = readPtr->length;
firstBlock = (unsigned int) readPtr->offset / FS_BLOCK_SIZE;
lastBlock = (unsigned int) (readPtr->offset + size - 1) /
FS_BLOCK_SIZE;
for (blockNum = firstBlock;
blockNum <= lastBlock; blockNum++) {
toRead = size;
if ((unsigned int) (readPtr->offset + size - 1)
/ FS_BLOCK_SIZE > blockNum) {
toRead = (blockNum + 1) * FS_BLOCK_SIZE - readPtr->offset;
}
Fscache_FetchBlock(&handlePtr->cacheInfo, blockNum,
FSCACHE_DATA_BLOCK, &blockPtr, &found);
fs_Stats.blockCache.readAccesses++;
if (found) {
if (blockPtr->timeDirtied != 0) {
fs_Stats.blockCache.readHitsOnDirtyBlock++;
} else {
fs_Stats.blockCache.readHitsOnCleanBlock++;
}
if (blockPtr->flags & FSCACHE_READ_AHEAD_BLOCK) {
fs_Stats.blockCache.readAheadHits++;
}
/*
* Update bytesRead after the successful FetchBlock. The
* bytesRead counter counts all bytes read from the cache,
* including misses.
*/
Fs_StatAdd(toRead, fs_Stats.blockCache.bytesRead,
fs_Stats.blockCache.bytesReadOverflow);
fs_Stats.rmtIO.hitsOnVMBlock++;
bcopy(blockPtr->blockAddr + (readPtr->offset &
FS_BLOCK_OFFSET_MASK), readPtr->buffer, toRead);
if (blockPtr->flags & FSCACHE_READ_AHEAD_BLOCK) {
fs_Stats.blockCache.readAheadHits++;
}
/*
* Let heap pages sit in the cache.
*/
if (readPtr->flags & FS_HEAP) {
fs_Stats.rmtIO.hitsOnHeapBlock++;
Fscache_UnlockBlock(blockPtr, 0, -1, 0,
FSCACHE_CLEAR_READ_AHEAD);
} else {
if (readPtr->flags & FS_SWAP) {
fs_Stats.rmtIO.hitsOnSwapPage++;
} else if ((readPtr->flags & (FS_HEAP | FS_SWAP)) == 0) {
/* It's a code page */
fs_Stats.rmtIO.hitsOnCodePage++;
}
Fscache_UnlockBlock(blockPtr, 0, -1, 0,
FSCACHE_CLEAR_READ_AHEAD | FSCACHE_BLOCK_UNNEEDED);
}
} else { /* Not found. */
fs_Stats.rmtIO.missesOnVMBlock++;
/*
* It's an initialized heap page. Read it into the
* cache.
*/
if ((readPtr->flags & FS_HEAP) &&
blockPtr != (Fscache_Block *) NIL) {
Fscache_FileInfo *cacheInfoPtr;
fs_Stats.rmtIO.missesOnHeapBlock++;
cacheInfoPtr = &handlePtr->cacheInfo;
status = (cacheInfoPtr->backendPtr->ioProcs.blockRead)
(cacheInfoPtr->hdrPtr, blockPtr, remoteWaitPtr);
if (status == SUCCESS) {
/* Copy to vm block */
bcopy(blockPtr->blockAddr + (readPtr->offset &
FS_BLOCK_OFFSET_MASK), readPtr->buffer, toRead);
/*
* Update bytesRead after the read into a cache block. The
* bytesRead counter counts all bytes read from the cache,
* including misses. We don't update bytesRead for the
* non-heap block reads, since those won't be read into
* the cache.
*/
Fs_StatAdd(toRead, fs_Stats.blockCache.bytesRead,
fs_Stats.blockCache.bytesReadOverflow);
fs_Stats.rmtIO.bytesReadForVM += toRead;
fs_Stats.rmtIO.bytesReadForHeap += toRead;
Fscache_UnlockBlock(blockPtr, 0, -1, 0,
FSCACHE_CLEAR_READ_AHEAD);
} else {
fs_Stats.blockCache.domainReadFails++;
}
}
if (status != SUCCESS || !(readPtr->flags & FS_HEAP) ||
blockPtr == (Fscache_Block *) NIL) {
if (blockPtr != (Fscache_Block *)NIL) {
Fscache_UnlockBlock(blockPtr, 0, -1, 0,
FSCACHE_DELETE_BLOCK);
}
if (readPtr->flags & FS_SWAP) {
fs_Stats.rmtIO.missesOnSwapPage++;
} else if ((readPtr->flags & (FS_HEAP | FS_SWAP)) == 0) {
/* It's a code page. */
fs_Stats.rmtIO.missesOnCodePage++;
}
readPtr->length = toRead;
status = Fsrmt_Read(streamPtr, readPtr, remoteWaitPtr,
replyPtr);
if (status != SUCCESS) {
break;
}
fs_Stats.rmtIO.bytesReadForVM += toRead;
if (readPtr->flags & FS_HEAP) {
fs_Stats.rmtIO.bytesReadForHeapUncached += toRead;
}
}
}
/*
* Successfully read one FS block, either remotely or from
* the cache. Update pointers and loop.
*/
size -= toRead;
readPtr->offset += toRead;
readPtr->buffer += toRead;
}
if (status != SUCCESS) {
readPtr->offset = savedOffset;
readPtr->length = savedLength;
readPtr->buffer = savedBuffer;
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* FsrmtFilePageWrite --
*
* Do a page-out for a remote file.
* NB: this knows that remote block allocation is a no-op, so
* there is no call to the allocation routine.
*
* Results:
* The results of Fsrmt_Write.
*
* Side effects:
* Statistics are taken. (This could be replaced with Fsrmt_Write
* if these statistics aren't needed.)
*
*----------------------------------------------------------------------
*/
ReturnStatus
FsrmtFilePageWrite(streamPtr, writePtr, remoteWaitPtr, replyPtr)
Fs_Stream *streamPtr; /* Open stream to remote file. */
Fs_IOParam *writePtr; /* Read parameter block */
Sync_RemoteWaiter *remoteWaitPtr; /* Process info for remote waiting */
Fs_IOReply *replyPtr; /* Signal to return, if any */
{
ReturnStatus status;
status = Fsrmt_Write(streamPtr, writePtr, remoteWaitPtr, replyPtr);
if (status == SUCCESS) {
fs_Stats.rmtIO.bytesWrittenForVM += replyPtr->length;
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* FsrmtFileBlockRead --
*
* Read in a cache block for a remote file.
*
* Results:
* The return code from the server.
*
* Side effects:
* This sets the blockPtr->blockSize to be the actual amount of
* data read into the block. Any left over space is zero filled.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
FsrmtFileBlockRead(hdrPtr, blockPtr, waitPtr)
Fs_HandleHeader *hdrPtr; /* Pointer to handle for file to write to. */
Fscache_Block *blockPtr; /* Block to read in. blockNum is the logical
* block and indicates the offset. blockSize
* is set to FS_BLOCK_SIZE, but we reduce
* that if less gets read. blockAddr is
* the memory area for the block. */
Sync_RemoteWaiter *waitPtr; /* For remote waiting if remote cache is full */
{
Fs_Stream dummyStream;
Fs_IOParam io;
Fs_IOReply reply;
ReturnStatus status;
dummyStream.hdr.fileID.type = -1;
dummyStream.ioHandlePtr = hdrPtr;
io.buffer = blockPtr->blockAddr;
io.length = FS_BLOCK_SIZE;
io.offset = blockPtr->blockNum * FS_BLOCK_SIZE;
io.flags = 0;
status = Fsrmt_Read(&dummyStream, &io, waitPtr, &reply);
blockPtr->blockSize = reply.length;
if (blockPtr->blockSize < FS_BLOCK_SIZE) {
/*
* We always must make sure that every cache block is filled
* with zeroes. Since we didn't read a full block zero fill
* the rest.
*/
fs_Stats.blockCache.readZeroFills++;
bzero(blockPtr->blockAddr + blockPtr->blockSize,
FS_BLOCK_SIZE - blockPtr->blockSize);
}
fs_Stats.rmtIO.bytesReadForCache += blockPtr->blockSize;
return(status);
}
/*
*----------------------------------------------------------------------
*
* FsrmtFileBlockWrite --
*
* Write out a cache block for a remote file. This understands
* that the diskBlock is the same as the (logical) blockNum
* of the block. It marks the write as coming from a client cache
* so the server doesn't update the modify time of the file.
*
*
* Results:
* The return code from the RPC.
*
* Side effects:
* The write.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
FsrmtFileBlockWrite(hdrPtr, blockPtr, flags)
Fs_HandleHeader *hdrPtr; /* I/O handle of file to write. */
Fscache_Block *blockPtr; /* The cache block to write. */
int flags;
{
ReturnStatus status;
Fs_Stream dummyStream;
Fs_IOParam io;
Fs_IOReply reply;
/*
* The server recognizes the write RPC as coming from the
* cache (FS_CLIENT_CACHE_WRITE) and ignores the streamID.
*/
dummyStream.hdr.fileID.type = -1;
dummyStream.ioHandlePtr = hdrPtr;
io.buffer = blockPtr->blockAddr;
io.length = blockPtr->blockSize;
io.offset = blockPtr->diskBlock * FS_BLOCK_SIZE; /* diskBlock == blockNum */
io.flags = flags | FS_CLIENT_CACHE_WRITE;
status = Fsrmt_Write(&dummyStream, &io, (Sync_RemoteWaiter *)NIL, &reply);
if (status == SUCCESS) {
fs_Stats.rmtIO.bytesWrittenFromCache += blockPtr->blockSize;
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* FsrmtFileBlockAllocate --
*
* Allocate disk space for a remote file. This always works, ie.
* the server is not contacted. The offset is just mapped to a
* logical block number for the file.
*
* IF THIS IS CHANGED TO ACTUALLY DO SOMETHING, then please add
* a call to this routine in FsrmtFilePageWrite.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
static ReturnStatus
FsrmtFileBlockAllocate(hdrPtr, offset, numBytes, flags, blockAddrPtr, newBlockPtr)
Fs_HandleHeader *hdrPtr; /* Local file handle. */
int offset; /* Offset to allocate at. */
int numBytes; /* Number of bytes to allocate. */
int flags; /* FSCACHE_DONT_BLOCK */
int *blockAddrPtr; /* Disk address of block allocated. */
Boolean *newBlockPtr; /* TRUE if there was no block allocated
* before. */
{
*newBlockPtr = FALSE;
*blockAddrPtr = offset / FS_BLOCK_SIZE;
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* FsrmtFileTrunc --
*
* Truncate a remote file. Nothing need be done is the remote case
* since all the work is done on the file server.
*
* Results:
* SUCCESS
*
* Side effects:
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
static ReturnStatus
FsrmtFileTrunc(hdrPtr, size, delete)
Fs_HandleHeader *hdrPtr; /* I/O handle for the file. */
int size; /* Size to truncate to. */
Boolean delete; /* True if the file is being deleted. */
{
return SUCCESS;
}
/*
*----------------------------------------------------------------------
*
* FsrmtFileIOControl --
*
* IOControls for remote regular files. Note, no byte order
* checking/fixing is down here as this is always called directly from
* a process, never via RPC.
*
* Results:
* An error code from the command.
*
* Side effects:
* Command dependent.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
FsrmtFileIOControl(streamPtr, ioctlPtr, replyPtr)
Fs_Stream *streamPtr; /* Stream to remote file */
Fs_IOCParam *ioctlPtr; /* I/O Control parameter block */
Fs_IOReply *replyPtr; /* Return length and signal */
{
register Fsrmt_FileIOHandle *handlePtr =
(Fsrmt_FileIOHandle *)streamPtr->ioHandlePtr;
register ReturnStatus status;
Fsutil_HandleLock(handlePtr);
switch(ioctlPtr->command) {
case IOC_REPOSITION: {
if (streamPtr->flags & FS_RMT_SHARED) {
status = Fsrmt_IOControl(streamPtr, ioctlPtr, replyPtr);
} else {
status = SUCCESS;
}
break;
}
case IOC_GET_FLAGS:
if (ioctlPtr->outBufSize >= sizeof(int)) {
*(int *)ioctlPtr->outBuffer = 0;
}
status = SUCCESS;
break;
case IOC_SET_FLAGS:
case IOC_SET_BITS:
case IOC_CLEAR_BITS:
status = SUCCESS;
break;
case IOC_TRUNCATE:
if ((streamPtr->flags & FS_WRITE) == 0) {
status = FS_NO_ACCESS;
} else if (ioctlPtr->inBufSize >= sizeof(int)) {
register int length = *(int *)ioctlPtr->inBuffer;
Fscache_Trunc(&handlePtr->cacheInfo, length, 0);
status = Fsrmt_IOControl(streamPtr, ioctlPtr, replyPtr);
} else {
status = GEN_INVALID_ARG;
}
break;
case IOC_MAP:
if (ioctlPtr->inBufSize >= sizeof(int)) {
status = Fsrmt_IOControl(streamPtr, ioctlPtr, replyPtr);
} else {
status = GEN_INVALID_ARG;
}
break;
case IOC_LOCK:
case IOC_UNLOCK:
status = Fsrmt_IOControl(streamPtr, ioctlPtr, replyPtr);
break;
case IOC_NUM_READABLE: {
register int bytesAvailable;
register int streamOffset;
if (ioctlPtr->outBufSize < sizeof(int)) {
return(GEN_INVALID_ARG);
}
streamOffset = *(int *)ioctlPtr->inBuffer;
bytesAvailable = handlePtr->cacheInfo.attr.lastByte + 1 -
streamOffset;
*(int *)ioctlPtr->outBuffer = bytesAvailable;
status = SUCCESS;
break;
}
case IOC_SET_OWNER:
case IOC_GET_OWNER:
status = GEN_NOT_IMPLEMENTED;
break;
case IOC_PREFIX:
status = SUCCESS;
break;
case IOC_WRITE_BACK: {
/*
* Write out the cached data for the file.
*/
Ioc_WriteBackArgs *argPtr = (Ioc_WriteBackArgs *)ioctlPtr->inBuffer;
Fscache_FileInfo *cacheInfoPtr = &handlePtr->cacheInfo;
if (ioctlPtr->inBufSize < sizeof(Ioc_WriteBackArgs)) {
status = GEN_INVALID_ARG;
} else {
int firstBlock, lastBlock;
int blocksSkipped;
int flags = 0;
if (argPtr->shouldBlock) {
flags |= FSCACHE_FILE_WB_WAIT;
}
if (argPtr->firstByte > 0) {
firstBlock = argPtr->firstByte / FS_BLOCK_SIZE;
} else {
firstBlock = 0;
}
if (argPtr->lastByte > 0) {
lastBlock = argPtr->lastByte / FS_BLOCK_SIZE;
} else {
lastBlock = FSCACHE_LAST_BLOCK;
}
status = Fscache_FileWriteBack(cacheInfoPtr, firstBlock,
lastBlock, flags, &blocksSkipped);
if (status == SUCCESS) {
/*
* Perform the IOC_WRITE_BACK on the file server to
* insure that the file is forced to disk.
*/
status = Fsrmt_IOControl(streamPtr, ioctlPtr, replyPtr);
#define FILE_SERVER_IOC_WRITE_BACK_BROKEN
#ifdef FILE_SERVER_IOC_WRITE_BACK_BROKEN
if (status == GEN_INVALID_ARG) {
/*
* Currently, IOC_WRITE_BACK don't work to machines
* with different byte orders. This has been fixed
* but until all file servers run the kernel we
* patch round the bug.
* THIS CODE CAN BE REMOVED when all file servers
* are running a version > 1.075.
*/
status = SUCCESS;
}
#endif /* FILE_SERVER_IOC_WRITE_BACK_BROKEN */
}
}
break;
}
default:
status = GEN_INVALID_ARG;
break;
}
Fsutil_HandleUnlock(handlePtr);
return(status);
}
/*
*----------------------------------------------------------------------
*
* FsrmtFileGetIOAttr --
*
* Update the attributes of a remotely served file from attributes
* that we have cached here. The file server will have contacted
* other clients that may be caching the file, but we still need
* to check our own version of the access/modify times that we
* (might) have cached.
*
* Results:
* SUCCESS - always returned.
*
* Side effects:
* Fills in the access time, modify time, and size from information
* we have cached here.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
FsrmtFileGetIOAttr(fileIDPtr, clientID, attrPtr)
Fs_FileID *fileIDPtr; /* ID on the file */
int clientID; /* Host ID of calling process */
Fs_Attributes *attrPtr; /* Attributes to update */
{
Fsrmt_FileIOHandle *handlePtr;
handlePtr = Fsutil_HandleFetchType(Fsrmt_FileIOHandle, fileIDPtr);
if (handlePtr != (Fsrmt_FileIOHandle *)NIL) {
Fscache_UpdateAttrFromCache(&handlePtr->cacheInfo, attrPtr);
Fsutil_HandleRelease(handlePtr, TRUE);
}
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* FsrmtFileSetIOAttr --
*
* Set the attributes of a remotely served file. This routine is
* called after the file server has been contacted, so its only
* job is to update attributes we have cached here on the client.
*
* Results:
* SUCCESS or FS_STALE_HANDLE.
*
* Side effects:
* Updates the access time, modify time, and size that
* we have cached here.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
FsrmtFileSetIOAttr(fileIDPtr, attrPtr, flags)
Fs_FileID *fileIDPtr; /* ID on the file */
Fs_Attributes *attrPtr; /* Attributes to update */
int flags; /* What attrs to set */
{
Fsrmt_FileIOHandle *handlePtr;
handlePtr = Fsutil_HandleFetchType(Fsrmt_FileIOHandle, fileIDPtr);
if (handlePtr != (Fsrmt_FileIOHandle *)NIL) {
Fscache_UpdateCachedAttr(&handlePtr->cacheInfo, attrPtr, flags);
Fsutil_HandleRelease(handlePtr, TRUE);
}
return(SUCCESS);
}
static void FsrmtCleanBlocks _ARGS_((ClientData data,
Proc_CallInfo *callInfoPtr));
/*
*----------------------------------------------------------------------
*
* FsrmtStartWriteBack --
*
* Start a block cleaner process for the specified domain.
*
* Results:
* TRUE if a block cleaner was started.
*
* Side effects:
* Number of block cleaner processes may be incremented.
*
* ----------------------------------------------------------------------------
*/
static Boolean
FsrmtStartWriteBack(backendPtr, fileFsynced)
Fscache_Backend *backendPtr; /* Backend to start writeback. */
Boolean fileFsynced; /* For ASPLOS only. */
{
LOCK_MONITOR;
if (fsrmtBlockCleaners < fscache_MaxBlockCleaners) {
Proc_CallFunc(FsrmtCleanBlocks, (ClientData) backendPtr, 0);
fsrmtBlockCleaners++;
UNLOCK_MONITOR;
return TRUE;
}
UNLOCK_MONITOR;
return FALSE;
}
/*
* ----------------------------------------------------------------------------
*
* Functions to clean dirty blocks.
*
* ----------------------------------------------------------------------------
*/
/*
* ----------------------------------------------------------------------------
*
* FsrmtCleanBlocks
*
* Write all blocks on the dirty list to disk. Called either from
* a block cleaner process or synchronously during system shutdown.
*
* Results:
* None.
*
* Side effects:
* The dirty list is emptied.
*
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
static void
FsrmtCleanBlocks(data, callInfoPtr)
ClientData data; /* Background flag. If TRUE it means
* we are called from a block cleaner
* process. Otherwise we being called
* synchrounously during a shutdown */
Proc_CallInfo *callInfoPtr; /* Not Used. */
{
Fscache_Block *blockPtr;
ReturnStatus status;
int lastDirtyBlock;
Fscache_FileInfo *cacheInfoPtr;
Fscache_Backend *backendPtr;
backendPtr = (Fscache_Backend *) data;
cacheInfoPtr = Fscache_GetDirtyFile(backendPtr, TRUE,
FileMatch, (ClientData) 0);
while (cacheInfoPtr != (Fscache_FileInfo *)NIL) {
blockPtr = Fscache_GetDirtyBlock(cacheInfoPtr, BlockMatch,
(ClientData) 0, &lastDirtyBlock);
while (blockPtr != (Fscache_Block *) NIL) {
/*
* Write the block.
*/
status = backendPtr->ioProcs.blockWrite
(cacheInfoPtr->hdrPtr, blockPtr,
lastDirtyBlock ? FS_LAST_DIRTY_BLOCK : 0);
Fscache_ReturnDirtyBlock( blockPtr, status);
blockPtr = Fscache_GetDirtyBlock(cacheInfoPtr, BlockMatch,
(ClientData) 0, &lastDirtyBlock);
}
Fscache_ReturnDirtyFile(cacheInfoPtr, FALSE);
cacheInfoPtr = Fscache_GetDirtyFile(backendPtr, TRUE,
FileMatch, (ClientData) 0);
}
FscacheBackendIdle(backendPtr);
LOCK_MONITOR;
fsrmtBlockCleaners--;
UNLOCK_MONITOR;
}
/*
* ----------------------------------------------------------------------------
*
* FsrmtReallocBlock --
*
* Reallocate a block which got a write error.
*
* Results:
* None.
*
* Side effects:
*
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
static void
FsrmtReallocBlock(data, callInfoPtr)
ClientData data; /* Block to move */
Proc_CallInfo *callInfoPtr; /* Not used. */
{
panic("FsrmtReallocBlock called\n");
}
/*
* ----------------------------------------------------------------------------
*
* BlockMatch --
*
* Cache backend block type match. Fsrmt doesn't care about the
* order of blocks returned by GetDirtyBlocks.
*
* Results:
* TRUE.
*
* Side effects:
*
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
static Boolean
BlockMatch( blockPtr, cleintData)
Fscache_Block *blockPtr;
ClientData cleintData;
{
return TRUE;
}
/*
* ----------------------------------------------------------------------------
*
* FileMatch --
*
* Cache backend files match. Fsrmt doesn't care about the
* order of files returned by GetDirtyFiles.
*
* Results:
* TRUE.
*
* Side effects:
*
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
static Boolean
FileMatch( cacheInfoPtr, cleintData)
Fscache_FileInfo *cacheInfoPtr;
ClientData cleintData;
{
return TRUE;
}
/*
*----------------------------------------------------------------------------
*
* FsrmtSetupFileReopen --
*
* Set up the data for an RPC to reopen a file handle.
*
* Results:
* Return status.
*
* Side effects:
* Data structure set up.
*
*----------------------------------------------------------------------------
*/
ReturnStatus
FsrmtSetupFileReopen(hdrPtr, paramsPtr)
Fs_HandleHeader *hdrPtr;
Address paramsPtr;
{
Fsrmt_FileIOHandle *rmtHandlePtr;
int numDirtyBlocks;
Fsio_FileReopenParams *reopenParamsPtr =
(Fsio_FileReopenParams *) paramsPtr;
Boolean okayToScavenge;
rmtHandlePtr = (Fsrmt_FileIOHandle *) hdrPtr;
numDirtyBlocks = Fscache_PreventWriteBacks(&rmtHandlePtr->cacheInfo);
Fscache_AllowWriteBacks(&rmtHandlePtr->cacheInfo);
/*
* Optimize out re-opens for files that aren't being waited
* on, that have no users, and that have no blocks in the cache.
* I don't call OkToScavenge from fsutilHandleScavenge.c, since I think
* it's okay to do this even if there's already an active scavenger,
* because it will have locked the handle if it's working on it.
*/
okayToScavenge = Fscache_OkToScavengeExceptDirty(&rmtHandlePtr->cacheInfo);
if (recov_SkipCleanFiles && (rmtHandlePtr->rmt.recovery.use.ref == 0 &&
okayToScavenge &&
!Fsutil_RecoveryNeeded(&rmtHandlePtr->rmt.recovery))) {
fs_Stats.recovery.reopensAvoided++;
return FS_RECOV_SKIP;
} else if ((rmtHandlePtr->rmt.recovery.use.ref == 0 && okayToScavenge &&
!Fsutil_RecoveryNeeded(&rmtHandlePtr->rmt.recovery))) {
Vm_FileChanged(&rmtHandlePtr->segPtr);
Fsutil_RecoverySyncLockCleanup(&rmtHandlePtr->rmt.recovery);
Fscache_InfoSyncLockCleanup(&rmtHandlePtr->cacheInfo);
Fscache_ReadAheadSyncLockCleanup(&rmtHandlePtr->readAhead);
Fsutil_HandleRemove(rmtHandlePtr);
fs_Stats.object.rmtFiles--;
fs_Stats.recovery.reopensAvoided++;
return FS_NO_HANDLE;
}
/* for debugging */
#define FS_HANDLE_INVALID 0x20
if (hdrPtr->flags & FS_HANDLE_INVALID) {
printf("Attempting to recover invalid file handle <%d,0x%x,0x%x>\n",
hdrPtr->fileID.serverID, hdrPtr->fileID.major,
hdrPtr->fileID.minor);
return FAILURE;
}
/* end for debugging */
reopenParamsPtr->flags = rmtHandlePtr->flags;
if (numDirtyBlocks > 0) {
reopenParamsPtr->flags |= FSIO_HAVE_BLOCKS;
}
reopenParamsPtr->fileID = hdrPtr->fileID;
reopenParamsPtr->fileID.type = FSIO_LCL_FILE_STREAM;
reopenParamsPtr->prefixFileID.type = NIL; /* not used */
reopenParamsPtr->use = rmtHandlePtr->rmt.recovery.use;
reopenParamsPtr->version = rmtHandlePtr->cacheInfo.version;
return SUCCESS;
}
/*
*----------------------------------------------------------------------------
*
* FsrmtFinishFileReopen --
*
* Do post-processing for a file handle after bulk reopen.
*
* Results:
* None.
*
* Side effects:
* Write-backs, etc., may occur.
*
*----------------------------------------------------------------------------
*/
void
FsrmtFinishFileReopen(hdrPtr, statePtr, status)
Fs_HandleHeader *hdrPtr;
Address statePtr;
ReturnStatus status;
{
Fsrmt_FileIOHandle *rmtHandlePtr;
int numDirtyBlocks;
Fsio_FileState *fileStatePtr = (Fsio_FileState *) statePtr;
rmtHandlePtr = (Fsrmt_FileIOHandle *) hdrPtr;
numDirtyBlocks = Fscache_PreventWriteBacks(&rmtHandlePtr->cacheInfo);
Fscache_AllowWriteBacks(&rmtHandlePtr->cacheInfo);
if (status != SUCCESS) {
if (numDirtyBlocks > 0) {
printf(
"Re-open failed <%x> with dirty blocks in cache, \"%s\" <%d,%d> server %d\n",
status, Fsutil_HandleName(hdrPtr), hdrPtr->fileID.major,
hdrPtr->fileID.minor, hdrPtr->fileID.serverID);
}
if (status != RPC_TIMEOUT &&
status != RPC_SERVICE_DISABLED) {
/*
* Nuke the cache because our caller will nuke the handle.
*/
Fscache_FileInvalidate(&rmtHandlePtr->cacheInfo, 0, FSCACHE_LAST_BLOCK);
}
} else {
/*
* Flush any lanquishing dirty blocks back to the server.
*/
if (numDirtyBlocks > 0) {
int skipped;
(void) Fscache_FileWriteBack(&rmtHandlePtr->cacheInfo, 0,
FSCACHE_LAST_BLOCK, 0, &skipped);
}
/*
* Update the handle - take care of cache flushes, updating cached
* attributes, etc.
*/
if (Fscache_UpdateFile(&rmtHandlePtr->cacheInfo,
rmtHandlePtr->rmt.recovery.use.write,
fileStatePtr->version, fileStatePtr->cacheable,
&fileStatePtr->attr)) {
Vm_FileChanged(&rmtHandlePtr->segPtr);
}
rmtHandlePtr->openTimeStamp = fileStatePtr->openTimeStamp;
}
return;
}